package wangjg.mda.tra;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.Version;

import wangjg.mda.model.obj.ModelDataType;
import wangjg.mda.model.obj.Model;
import wangjg.mda.model.obj.ModelField;
import wangjg.test.mda.EntityDemo;

public class JClassUtil {
	private Class<?> cls;
	
	public JClassUtil() {
	}

	public JClassUtil(Class<?> cls) {
		this.cls = cls;
	}

	public Model getEntityModel() throws Exception{
		Model model = this.getModel();
		model.setFields(this.getModelFields());
        return model;
	}

	private Model getModel() {
		Model model = new Model();
	
		model.setName(cls.getSimpleName());
		model.setPackage(cls.getPackage().getName());
		
		if(Object.class.equals(cls.getSuperclass())!=true){
			model.setParent(cls.getSuperclass().getName());
		}
		
		Table table = cls.getAnnotation(Table.class);
		if(table!=null&&table.name()!=null&&table.name().length()!=0){
			String tableName = table.name();
		}
		
		return model;
	}

	private List<ModelField> getModelFields() throws Exception{
		List<ModelField> list = new ArrayList<ModelField>();
		List<PropertyInfo> orPiList = getOrderedPropertyInfoMap();
		for(PropertyInfo pi:orPiList){
			ModelField ef = this.getModelField(pi);
			if(ef!=null){
				list.add(ef);
			}
		}
		return list;
	}
	
	private List<PropertyInfo> getOrderedPropertyInfoMap() throws Exception{
		List<PropertyInfo> relist = new ArrayList<PropertyInfo>();
		Map<String ,PropertyInfo> piMap = getPropertyInfoMap();
		List<Field> list = this.getFieldList(cls, true);
		for(int i=0;i<list.size();i++){
			Field field = list.get(i);
			PropertyInfo pi = piMap.get(field.getName());
			if(pi==null){
				continue;
			}
			pi.setField(field);
			pi.setOrderNo(list.size()-i-1);
			relist.add(pi);
		}
		return relist;
	}
	
	private Map<String ,PropertyInfo> getPropertyInfoMap() throws Exception{
		Map<String ,PropertyInfo> map = new HashMap<String ,PropertyInfo>();
        BeanInfo beanInfo = Introspector.getBeanInfo(cls);
        PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
        for(PropertyDescriptor pd:pds){
        	PropertyInfo pi = new PropertyInfo();
        	pi.setName(pd.getName());
        	pi.setField(null);
        	pi.setOrderNo(0);
        	pi.setType(pd.getPropertyType());
        	pi.setReadMethod(pd.getReadMethod());
        	pi.setWriteMethod(pd.getWriteMethod());
        	
        	map.put(pi.getName(), pi);
        }
		return map;
	}
	
	private ModelField getModelField(PropertyInfo pi) {
		boolean isId = pi.getAnnotation(Id.class)!=null;
		boolean isAuto = pi.getAnnotation(GeneratedValue.class)!=null;
		boolean isVersion = pi.getAnnotation(Version.class)!=null;
		boolean isTransient = pi.getAnnotation(Transient.class)!=null;
    	Column column = pi.getAnnotation(Column.class);
    	String col_name = (column!=null&&column.name()!=null&&column.name().length()>0) ? column.name() : pi.getName();
    	
    	if(pi.getWriteMethod()==null && !isVersion){//�ų�
    		return null;
    	}
		
		ModelField field = new ModelField();
    	field.setName(pi.getName());
    	field.setType(getObjType(pi));
    	field.setId(isId);
    	field.setAuto(isAuto);
//    	field.setColumnName(col_name);
    	field.setTransient(isTransient);
		field.setVersion(isVersion);
    	if(column!=null){
    		if(String.class.equals(pi.getType())){
    			field.setLength(column.length());
    		}
        	field.setNullable(column.nullable());
        	field.setUnique(column.unique());
    	}
		return field;
	}
	
	public static final String getObjType(PropertyInfo pi){
		Class type = pi.getType();
		if(java.util.Date.class.isAssignableFrom(type)){
			type = java.util.Date.class;
		}
		String typeName = type.getName();
		if(ModelDataType.isPrimitive(type.getSimpleName())){
			typeName = type.getSimpleName();
		}
		return typeName;
	}
	
	/*
	 * 取得字段
	 * 根据字段顺序排序
	 */
	private List<Field> getFieldList(Class<?> c, boolean asc) {
		List<Field> list = new ArrayList<Field>();
		for (Class<?>cc=c; cc!=Object.class; cc=cc.getSuperclass()) {
			Field[] fields= cc.getDeclaredFields();
			if(asc){
				for(int i=0; i<fields.length; i++){
					Field field = fields[i];
					list.add(field);
				}
			}else{
				for(int i=fields.length-1; i>=0; i--){
					Field field = fields[i];
					list.add(field);
				}
			}
		}
		return list;
	}

	public static void main(String args[]) throws Exception{
		JClassUtil e = new JClassUtil(EntityDemo.class);
		Object obj = e.getEntityModel();
		System.out.println(obj);
	}
	
	class PropertyInfo{
		private String name;
		private Field field;
		private int orderNo;//order no
		private Class type;
		private Method readMethod;
		private Method writeMethod;
		
		
		public PropertyInfo() {
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public Field getField() {
			return field;
		}
		public void setField(Field field) {
			this.field = field;
		}
		public int getOrderNo() {
			return orderNo;
		}
		public void setOrderNo(int fieldNo) {
			this.orderNo = fieldNo;
		}
		public Class getType() {
			return type;
		}
		public void setType(Class propertyType) {
			this.type = propertyType;
		}
		public Method getReadMethod() {
			return readMethod;
		}
		public void setReadMethod(Method readMethod) {
			this.readMethod = readMethod;
		}
		public Method getWriteMethod() {
			return writeMethod;
		}
		public void setWriteMethod(Method writeMethod) {
			this.writeMethod = writeMethod;
		}
		public <A extends Annotation> A getAnnotation(Class<A> annotationClass){
			Annotation anno = readMethod.getAnnotation(annotationClass);
	    	if(anno==null){
	    		try {
					anno = field.getAnnotation(annotationClass);
				} catch (Exception e) {
					e.printStackTrace();
				}
	    	}
			return (A) anno;
		}
		@Override
		public String toString() {
			return "PropertyInfo [name=" + name + ", orderNo=" + orderNo
					+ ", type=" + type + "]";
		}

	}
}
